<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>数据驱动和响应式</title>
	<link rel="stylesheet" href="">
</head>
<body>
	<div id="app">
        <h1>我是{{people[0].name}}，今年{{people[0].age}}岁</h1>
        <h1>我是{{people[1].name}}，今年{{people[1].age}}岁</h1>
        <h1>我是{{people[2].name}}，今年{{people[2].age}}岁</h1>
    </div>
</body>
<script src="004.js"></script>
<script src="006.js"></script>
<script src="013.js"></script>
<script>

	class CHVue {
		constructor(option) {
			// 每次更新视图后，旧的dom就不存在了，因此我们需要动态获取dom
			this._el = ()=>document.querySelector(option.el);	
			this._data = option.data;
			this._parent = this._el().parentNode;

			this.response(this._data);
			this.mount();	// 挂载
		}

		mount() {
			// 1. 该render方法的作用是产生一个具有缓存作用的虚拟dom
			this.render = this.createRenderFn();
			this.mountComponent();
		}

		mountComponent() {
			this.update(this.render());
		}

		// 创造一个render方法，目的是为了缓存虚拟dom
		createRenderFn() {
			let vnode = createNode(this._el());

			// 将数据和vnode合并
			return function() {
				let vnodeWithData = {};
				this.deepClone(vnode, vnodeWithData);
				this.createNodeByData(vnodeWithData);
				return vnodeWithData;
			}
		}

		// 将虚拟dom渲染为真实的dom， vue使用了diff算法
		update(vnode) {
			let dom = this.createDom(vnode);
			this._parent.replaceChild(dom, this._el());
		}

		// 将数据和带坑的虚拟dom结合起来
		createNodeByData(vnode) {
			let children = vnode.children;

			for(let i = 0; i < children.length; i++) {
				let type = children[i].type;

				if(type === 1) {
					this.createNodeByData(children[i]);
				} else if(type === 3) {
		            let value = children[i].value;
					value = value.replace(/\{\{(.*?)\}\}/g, (_, g)=>{
		                return getPathData(this._data, g);
		            })
					children[i].value = value;
				}
			}
		}

		// 将虚拟的dom转换为真实的dom
		createDom(vnode) {
			let dom;	// 真实的dom
			let type = vnode.type;

			if(type === 1) {
				let data = vnode.data;
				let children = vnode.children;
				dom = document.createElement(vnode.name);

				// 遍历属性值
				data.forEach(val=>{
					let key = Object.keys(val);
					dom.setAttribute(key, val[key]);
				});

				// 使用递归函数创建子元素
				for(let i = 0; i < children.length; i++) {
					dom.appendChild(this.createDom(children[i]));
				}
			} 
			else if(type === 3) {
				dom = document.createTextNode(vnode.value);
			}

			return dom;
		}

		// 响应式原理
		defineResponse(obj, key, value, enumerable) {
			Object.defineProperty(obj, key, {
				enumerable: enumerable == undefined ? true : !!enumerable,
				get: ()=>{
					console.log(`读取了${value}`);
					return value;
				},
				set: (newValue)=>{
					value = newValue;
					console.log(`设置${key}的值为${value}`);
					this.mountComponent();
				}
			})
		}

		// 执行响应式操作
		response(obj) {
			let keys = Object.keys(obj);

			for(let i = 0; i < keys.length; i++) {
				if(typeof(obj[keys[i]]) === 'object' ) {
					if(Array.isArray(obj[keys[i]])) {	// 如果使数组，则修改原型链
						obj[keys[i]].__proto__ = myArray.prototype;
					}
					this.response(obj[keys[i]]);
				} else {
					this.defineResponse(obj, keys[i], obj[keys[i]]);
				}
			}

		}

		deepClone(obj1, obj2) {
			let keys = Object.keys(obj1);

			for(let i = 0; i < keys.length; i++) {
				if(typeof obj1[keys[i]] === 'object') {
					if (Array.isArray(obj1[keys[i]])) {
						obj2[keys[i]] = [];
					} else {
						obj2[keys[i]] = {};
					}
					this.deepClone(obj1[keys[i]], obj2[keys[i]]);
				} else {
					obj2[keys[i]] = obj1[keys[i]];
				}
			}
		}
	}





	let app = new CHVue({
		el: '#app',
		data: {
			people: [
				{
					name: 'a',
					age: 1
				},
				{
					name: 'b',
					age: 2
				},
				{
					name: 'c',
					age: 3
				},
			]
		}
	})

</script>
</html>